import { u8, u64, i64, PAGE_SIZE, MemoryProtection, toU64, toI64 } from '../types';

// 页表项
interface PageTableEntry {
  address: u64;  // 物理地址
  protection: MemoryProtection;  // 保护标志
  present: boolean;  // 是否存在
  dirty: boolean;  // 是否被修改
}

// 内存区域描述
interface MemoryRegion {
  start: u64;
  end: u64;
  protection: MemoryProtection;
  name?: string;  // 区域名称（调试用）
}

export class MemoryManager {
  private pages: Map<bigint, Uint8Array>;  // 页表
  private pageTableEntries: Map<bigint, PageTableEntry>;  // 页表项
  private memoryRegions: MemoryRegion[];  // 内存区域记录

  constructor() {
    this.pages = new Map();
    this.pageTableEntries = new Map();
    this.memoryRegions = [];
  }

  // 分配一页内存
  private allocatePage(): Uint8Array {
    return new Uint8Array(PAGE_SIZE);
  }

  // 获取页地址
  private getPageAddress(addr: u64): u64 {
    return toU64(Number(addr) & ~(PAGE_SIZE - 1));
  }

  // 获取页内偏移
  private getPageOffset(addr: u64): number {
    return Number(addr) & (PAGE_SIZE - 1);
  }

  // 确保页存在
  private ensurePageExists(pageAddr: u64, protection: MemoryProtection = MemoryProtection.READ_WRITE_EXECUTE): void {
    if (!this.pages.has(pageAddr)) {
      const page = this.allocatePage();
      this.pages.set(pageAddr, page);
      this.pageTableEntries.set(pageAddr, {
        address: pageAddr,
        protection,
        present: true,
        dirty: false
      });
    }
  }

  // 映射内存区域
  map(startAddr: u64, size: number, protection: MemoryProtection = MemoryProtection.READ_WRITE_EXECUTE): void {
    const endAddr = toU64(Number(startAddr) + size);
    
    // 记录内存区域
    this.memoryRegions.push({
      start: startAddr,
      end: endAddr,
      protection
    });

    // 分配并映射所有需要的页
    let currentAddr = startAddr;
    while (currentAddr < endAddr) {
      this.ensurePageExists(this.getPageAddress(currentAddr), protection);
      currentAddr = toU64(Number(currentAddr) + PAGE_SIZE);
    }
  }

  // 读取内存
  read(addr: u64, size: number): Uint8Array {
    const result = new Uint8Array(size);
    let bytesRead = 0;
    
    while (bytesRead < size) {
      const pageAddr = this.getPageAddress(addr);
      const pageOffset = this.getPageOffset(addr);
      const remainingInPage = PAGE_SIZE - pageOffset;
      const bytesToRead = Math.min(remainingInPage, size - bytesRead);
      
      const page = this.pages.get(pageAddr);
      if (!page) {
        throw new Error(`Segmentation fault: read at invalid address ${addr}`);
      }
      
      // 检查权限
      const pte = this.pageTableEntries.get(pageAddr);
      if (pte && !(pte.protection & MemoryProtection.READ)) {
        throw new Error(`Protection fault: read access denied at address ${addr}`);
      }
      
      // 读取数据
      result.set(page.subarray(pageOffset, pageOffset + bytesToRead), bytesRead);
      
      // 更新指针和计数器
      addr = toU64(Number(addr) + bytesToRead);
      bytesRead += bytesToRead;
    }
    
    return result;
  }

  // 写入内存
  write(addr: u64, data: Uint8Array): void {
    let bytesWritten = 0;
    const size = data.length;
    
    while (bytesWritten < size) {
      const pageAddr = this.getPageAddress(addr);
      const pageOffset = this.getPageOffset(addr);
      const remainingInPage = PAGE_SIZE - pageOffset;
      const bytesToWrite = Math.min(remainingInPage, size - bytesWritten);
      
      const page = this.pages.get(pageAddr);
      if (!page) {
        throw new Error(`Segmentation fault: write at invalid address ${addr}`);
      }
      
      // 检查权限
      const pte = this.pageTableEntries.get(pageAddr);
      if (pte && !(pte.protection & MemoryProtection.WRITE)) {
        throw new Error(`Protection fault: write access denied at address ${addr}`);
      }
      
      // 写入数据
      page.set(data.subarray(bytesWritten, bytesWritten + bytesToWrite), pageOffset);
      
      // 标记页为脏
      if (pte) {
        pte.dirty = true;
      }
      
      // 更新指针和计数器
      addr = toU64(Number(addr) + bytesToWrite);
      bytesWritten += bytesToWrite;
    }
  }

  // 设置内存保护
  protect(addr: u64, size: number, protection: MemoryProtection): void {
    let currentAddr = addr;
    const endAddr = toU64(Number(addr) + size);
    
    while (currentAddr < endAddr) {
      const pageAddr = this.getPageAddress(currentAddr);
      const pte = this.pageTableEntries.get(pageAddr);
      
      if (pte) {
        pte.protection = protection;
      }
      
      currentAddr = toU64(Number(currentAddr) + PAGE_SIZE);
    }
  }

  // 释放内存
  free(addr: u64, size: number): void {
    let currentAddr = addr;
    const endAddr = toU64(Number(addr) + size);
    
    while (currentAddr < endAddr) {
      const pageAddr = this.getPageAddress(currentAddr);
      this.pages.delete(pageAddr);
      this.pageTableEntries.delete(pageAddr);
      
      currentAddr = toU64(Number(currentAddr) + PAGE_SIZE);
    }
    
    // 更新内存区域记录
    this.memoryRegions = this.memoryRegions.filter(region => 
      region.end <= addr || region.start >= endAddr
    );
  }

  // 检查内存是否有效
  isValidAddress(addr: u64, size: number): boolean {
    const endAddr = toU64(Number(addr) + size);
    
    // 检查是否与任何有效内存区域重叠
    for (const region of this.memoryRegions) {
      if (addr < region.end && endAddr > region.start) {
        return true;
      }
    }
    
    return false;
  }

  // 获取内存使用统计
  getMemoryStats(): {
    totalPages: number;
    usedPages: number;
    memoryRegions: number;
  } {
    return {
      totalPages: Math.ceil(this.memoryRegions.reduce((sum, region) => {
        return sum + Number(region.end) - Number(region.start);
      }, 0) / PAGE_SIZE),
      usedPages: this.pages.size,
      memoryRegions: this.memoryRegions.length
    };
  }
}